#include "kerl.h"
#include "conversion.h"
#include "common.h"

//extern Conversion* pConversion;

namespace Kerl {

	void initialize(Keccak384 *sha3)
	{
		cx_keccak_init(sha3, 384);
	}

	void reinitialize(Keccak384 *sha3, const unsigned char *state_bytes)
	{
		initialize(sha3);
		absorbChunk(sha3, state_bytes);
	}

	void absorbBytes(Keccak384 *sha3, const unsigned char *bytes,
						   unsigned int len)
	{
		cx_hash(sha3, 0, (unsigned char *)bytes, len, NULL, 0);
	}

	void absorbChunk(Keccak384 *sha3, const unsigned char *bytes)
	{
		absorbBytes(sha3, bytes, CX_KECCAK384_SIZE);
	}

	void squeezeFinalChunk(Keccak384 *sha3, unsigned char *bytes)
	{
		cx_hash(sha3, CX_LAST, bytes, 0, bytes, CX_KECCAK384_SIZE);
		Conversion conv;
		conv.bytes_set_last_trit_zero(bytes);
	}

	void squeezeChunk(Keccak384 *sha3, unsigned char *bytes)
	{
		unsigned char state_bytes[CX_KECCAK384_SIZE];

		stateSqueezeChunk(sha3, state_bytes, bytes);
		reinitialize(sha3, state_bytes);
	}

	void squeezeBytes(Keccak384 *sha3, unsigned char *bytes, unsigned int len)
	{
		// absorbing happens in 48 word bigint chunks
		for (unsigned int i = 0; i < (len / CX_KECCAK384_SIZE); i++) {
			squeezeChunk(sha3, bytes + CX_KECCAK384_SIZE * i);
		}
	}

	static inline void flipHashBytes(unsigned char *bytes)
	{
		for (unsigned int i = 0; i < CX_KECCAK384_SIZE; i++) {
			bytes[i] = ~bytes[i];
		}
	}

	void stateSqueezeChunk(Keccak384 *sha3, unsigned char *state_bytes,
								  unsigned char *bytes)
	{
		cx_hash(sha3, CX_LAST, state_bytes, 0, state_bytes,
				CX_KECCAK384_SIZE);

		os_memcpy(bytes, state_bytes, CX_KECCAK384_SIZE);
		Conversion conv;
		conv.bytes_set_last_trit_zero(bytes);

		// flip bytes for multiple squeeze
		flipHashBytes(state_bytes);
	}

}
